#!/usr/bin/env python
# -*- encoding=utf-8 -*-
'''
Created on 2017/3/8

@author: x00173555
'''


import re,urllib2,os,commands
import sys
from xml.dom import minidom
from subprocess  import *
import getopt
import urllib
import time
from xml.etree import ElementTree as ET
import threading
import multiprocessing
from multiprocessing import cpu_count
from multiprocessing import Manager
from datetime import datetime

projects = []
manager = Manager()
package_with_service = manager.list()
building_packages = []

def usage():
    print "sys.argv[0] [-s service_list.xml][-t time][-n nobuild]"

def shell_cmd(s_cmd_line, inmsg=None):
    p = Popen(s_cmd_line, stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=True)
    if inmsg:
        p.stdin.write(inmsg)
    out, err = p.communicate()
    return p.returncode, out, err

def rebuild_pkg(project, failed_package, repo, arch):
    cmd = 'osc rebuild %s %s %s %s'  % (project, failed_package, repo, arch)
    ret, out, err = shell_cmd(cmd)
    if ret != 0:
        time.sleep(2)
        ret, out, err = shell_cmd(cmd)
        if ret != 0:
            time.sleep(4)
            ret, out, err = shell_cmd(cmd)
            if ret != 0:
                print "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err)
                return False           
    print "%s : OK" % (cmd)
    return True

def run_service(project, package, flag=True):
    run_list = project + ' ' + package
    if flag == True:
        if run_list in package_with_service:
            return True
    cmd = 'osc service remoterun %s %s' % (project, package)
    ret, out, err = shell_cmd(cmd)
    if ret != 0:
        if flag == True:
            cmd1 = 'echo "%s" | grep "no source service defined"' % err
            ret, out, err = shell_cmd(cmd1)
            if ret == 0:
                return True
        time.sleep(2)
        ret, out, err = shell_cmd(cmd)
        if ret != 0:
            if flag == True:
                cmd1 = 'echo "%s" | grep "no source service defined"' % err
                ret, out, err = shell_cmd(cmd1)
                if ret == 0:
                    return True   
            time.sleep(4)
            ret, out, err = shell_cmd(cmd)
            if ret != 0:
                if flag == True:
                    cmd1 = 'echo "%s" | grep "no source service defined"' % err
                    ret, out, err = shell_cmd(cmd1)
                    if ret == 0:
                        return True
                time.sleep(8)
                ret, out, err = shell_cmd(cmd)
                if ret != 0:
                    if flag == True:
                        cmd1 = 'echo "%s" | grep "no source service defined"' % err
                        ret, out, err = shell_cmd(cmd1)
                        if ret == 0:
                            return True
                    print "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err)
                    return False
    print "%s : OK" % (cmd)
    package_with_service.append(run_list)
    return True

def run_package_with_service(builds):
    result = []
    pool = multiprocessing.Pool(processes=cpu_count())
    for build in builds:
        project_name = build[0]
        repo = build[1]
        arch = build[2]
        exclude_package = build[3]
        include_package = build[4]
        run_all = build[5]
        if ( run_all == 'yes' or run_all == 'YES'):
            cmd = 'osc ls %s > /tmp/package_list' % (project_name)
            ret, out, err = shell_cmd(cmd)
            if ret != 0:
                print "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err)
                sys.exit(1)
            try:
                package_list = open('/tmp/package_list')
            except:
                print "can't open file of /tmp/package_list"
                sys.exit(1)

            disabled_package_list = []
            cmd = 'osc prjresults %s --csv --show-excluded --repo=%s --arch=%s | sed "1d" | egrep "disabled$|excluded$"' % (project_name, repo, arch)
            ret, out, err = shell_cmd(cmd)
            if ret == 0:
                for out_result in out.splitlines():
                    disabled_package_list.append(out_result.split(';')[0])

            for package in package_list:
                package = package.strip('\n')
                if package in exclude_package:
                    continue
                if package in disabled_package_list:
                    continue
                if project_name + ' ' + package in package_with_service:
                    continue
                result.append(pool.apply_async(run_service, (project_name, package)))
        else:
            for package in include_package:
                result.append(pool.apply_async(run_service, (project_name, package)))

    pool.close()
    pool.join()
    for res in result:
        if res.get() != True:
            print "All run_service done. but some failed."
            sys.exit(1)

def wait_result(builds):
    wait_time = 0
    for build in builds:
        project = build[0]
        repo = build[1]
        arch = build[2]
        cmd = 'osc r --csv %s --repo=%s --arch=%s 2> /dev/null | head -n1 | grep "published$" &> /dev/null' % (project, repo, arch)
        cmd1 = 'osc r --csv %s --repo=%s --arch=%s | sed "1d" | egrep "broken$|blocked$|failed$"' % (project, repo, arch)
        ret1, out1, err1 = shell_cmd(cmd1)
        rerun_num = 0
        blocked_rerun_num = 0
        failed_rerun_num = 0
        while os.system(cmd) != 0 or ret1 == 0:
            if wait_time % 60 == 0:
                print "Waiting for %s --repo=%s --arch=%s to be build finished" % (project, repo, arch)

            #check broken package,than rerun it
            cmd2 = 'osc r --csv %s --repo=%s --arch=%s | sed "1d" | grep "broken$"' % (project, repo, arch)
            ret, out, err = shell_cmd(cmd2)
            if ret == 0 and rerun_num <= 5:
                print "broken packages: %s %s" % (project, out)
                for out_result in out.splitlines():
                    broken_package = out_result.split(';')[0]
                    print "rerun broken package: %s" % (broken_package)
                    if broken_package != '':
                        if (run_service(project, broken_package, flag=False) == False):
                            sys.exit(1)
                rerun_num = rerun_num + 1
                if rerun_num > 5:
                    break

            # check blocked
            if wait_time >= 3600:
                cmd3 = 'osc r --csv %s --repo=%s --arch=%s | sed "1d" | grep "blocked$"' % (project, repo, arch)
                ret3, out3, err3 = shell_cmd(cmd3)
                if ret3 == 0 and blocked_rerun_num <= 3:
                    run_service_flag = 0
                    for out_result in out3.splitlines():
                        blocked_package = out_result.split(';')[0]
                        if blocked_package == '':
                            continue
                        cmd4 = 'osc api /source/%s/%s | grep \'code="running"\'' % (project, blocked_package)
                        ret4, out4, err4 = shell_cmd(cmd4)
                        if ret4 != 0:
                            continue
                        else:
                            cmd5 = 'osc api /statistics/updated_timestamp/%s/%s | egrep "updated=\".*\"" ' % (project, blocked_package)
                            ret5, out5, err5 = shell_cmd(cmd5)
                            updated_timestamp = out5.split('"')[5]
                            time1 = datetime.strptime(updated_timestamp, "%Y-%m-%dT%H:%M:%SZ")
                            time2 = datetime.utcnow()
                            print 'running service in process ==== %s' % out5
                            if ret5 == 0 and time2 > time1:
                                timespan = (time2 -time1).seconds
                            else:
                                timespan = 0
                            if timespan > 1800:
                                print "rerun blocked package in service process: %s" % (blocked_package)
                                if (run_service(project, blocked_package, flag=False) == False):
                                    print 'run_service at rerun blocked_package %s %s failed' % (project, blocked_package)
                                    sys.exit(1)
                                run_service_flag = 1
                    if run_service_flag == 1:
                        blocked_rerun_num = blocked_rerun_num + 1
                    if blocked_rerun_num > 3:
                        break

            # check failed package and try rerun service
            cmd4 = 'osc r --csv %s --repo=%s --arch=%s | sed "1d" | grep "failed$"' % (
                project, repo, arch)
            ret4, out4, err4 = shell_cmd(cmd4)
            if os.system(cmd) == 0 and ret4 == 0:
                if failed_rerun_num <= 2:
                    for out_result in out4.splitlines():
                        failed_package = out_result.split(';')[0]
                        if failed_package != '':
                            cmd5='osc remotebuildlog %s/%s/%s/%s | egrep "vfork: Resource|Waiting for unfinished jobs"' % (project, failed_package, repo, arch)
                            ret5, out5, err5 = shell_cmd(cmd5)
                            if ret5 == 0:
                                if (rebuild_pkg(project, failed_package, repo, arch) == False):
                                    sys.exit(1)
                    failed_rerun_num = failed_rerun_num + 1
                else:
                    print "failed packages: %s %s" % (project, out4)
                    break

            time.sleep(20)
            wait_time = wait_time + 20
            ret1, out1, err1 = shell_cmd(cmd1)

        print 'Project: %s arch: %s --- build is finished' % (project, arch)

# get building project package
def get_building_project_package(builds):
    while True:
        for build in builds:
            project = build[0]
            repo = build[1]
            arch = build[2]
            cmd = 'osc r --csv %s --repo=%s --arch=%s | sed "1d" | egrep "building$"' % (project, repo, arch)
            ret, out, err = shell_cmd(cmd)
            if ret == 0:
                for out_result in out.splitlines():
                    building_package = out_result.split(';')[0]
                    tmp_str = project + ' ' + building_package
                    if tmp_str not in building_packages:
                        building_packages.append(tmp_str)
                        print "building packages: %s %s" % (project, building_package)

        time.sleep(30)


def get_result(builds, build_fail):
    for build in builds:
        project = build[0]
        repo = build[1]
        arch = build[2]
        cmd1 = 'osc r --csv %s --repo=%s --arch=%s 2> /dev/null | head -n1 | grep "published$" &> /dev/null' % (
            project, repo, arch)
        cmd2 = 'osc r --csv %s --repo=%s --arch=%s | sed "1d" | egrep "blocked$" &> /dev/null' % (
            project, repo, arch)
        wait_time = 0
        while True:
            if wait_time % 180 == 0:
                print "Waiting for %s --repo=%s --arch=%s to be build finished" % (project, repo, arch)
            if os.system(cmd1) == 0 and os.system(cmd2) != 0:
                break
            time.sleep(30)
            wait_time = wait_time + 30

        cmd = "osc r --csv %s --repo=%s --arch=%s 2> /dev/null | egrep ';failed$|;broken$|;unresolvable$|;unknown$'" % (
        project, repo, arch)
        (status, output) = commands.getstatusoutput(cmd)
        if status == 0:
            str = output.split('\n')
            for i in range(len(str)):
                print "Error: build on %s %s %s %s" % (project, str[i], repo, arch)
                build_fail[0] = build_fail[0] + 1


if __name__ == "__main__":
    if len(sys.argv) < 2:
        usage()
        exit(1)

    try:
        options,args = getopt.getopt(sys.argv[1:],"s:t:n",["service=","time=","nobuild"])
    except getopt.GetoptError:
        sys.exit(1)


    sleep_time = 120
    for name,value in options:
        if name in ('-s', '--service'):
            service_list = value
            run_all_service = 0
        if name in ('-t', '--time'):
            sleep_time = int(value)
        if name in ('-n', '--nobuild'):
            nobuild_flag = True
        else:
            nobuild_flag = False

    if os.path.isfile(service_list) == False:
        print service_list, "is not a file"
        exit(1)
    try:
        xmldoc = minidom.parse(service_list)
    except:
        print "Can't parse Xml File of %s.\n" % (service_list)
        exit(1)
    project = xmldoc.getElementsByTagName('project')
    for i in range(0, len(project)):
        project_name = project[i].attributes['name'].value
        project_repo = project[i].attributes['repo'].value
        project_arch = project[i].attributes['arch'].value
        run_all = project[i].attributes['run_all'].value
        exclude_package = []
        include_package = []
        if (run_all == 'yes' or run_all == 'YES'):
            for package in project[i].childNodes:
                if package.nodeType == package.ELEMENT_NODE:
                    if package.nodeName == 'exclude_package':
                        package_name = package.attributes['name'].value
                        exclude_package.append(package_name)
        else:
            for package in project[i].childNodes:
                if package.nodeType == package.ELEMENT_NODE:
                    if package.nodeName == 'package':
                        package_name = package.attributes['name'].value
                        include_package.append(package_name)
        projects.append( [ project_name, project_repo, project_arch, exclude_package, include_package, run_all ] )

    # run in background for get the project:package has been building
    threads = []
    t1 = threading.Thread(target=get_building_project_package, args=(projects,))
    threads.append(t1)
    # start threads
    for t in threads:
        t.setDaemon(True)
        t.start()

    if nobuild_flag == False:
        run_package_with_service(projects)           

    print 'waiting build to be finished --- sleep %d' % (sleep_time)
    time.sleep(sleep_time)
    wait_result(projects)

    logfile = 'building.log'
    ls = os.linesep
    fobj = open( logfile, 'w' )
    fobj.writelines(['building packages: %s%s' % ( building_package, ls ) for building_package in building_packages ])
    fobj.close()
    print "The project package has been building:"
    for i in range(len(building_packages)):
        print "\t%s" % (building_packages[i])

    build_fail = [0]
    get_result(projects, build_fail)
    if build_fail[0] > 0:
        print "%d packages failed on obs build" % (build_fail[0])
        sys.exit(1)
    print 'All build succeeded'
